/**
  Description:
  
  This example is inspired by a real scala class
  src/compiler/scala/tools/nsc/typechecker/Typers.scala from scalac.
  The point of this example is the definition of a common extractor
  "UnderlyingType" to retrieve the underlying type of some classes.
*/

/* constraint
	Dom_NarrowableType = (TypeRef \Un RefinedType) &
	Dom_UnderlyingType  =  (ExistentialType \Un AnnotatedType \Un PolyType)  
*/
  
sealed abstract class Type
class TypeRef(val pre: Type, val sym: Symbol, val args: List[Type]) extends Type
class RefinedType(val parents: List[Type],val decls: Scope) extends Type
class ExistentialType(val quantified: List[Symbol], val underlying: Type) extends Type
class AnnotatedType(val attributes: List[AnnotationInfo],val underlying: Type) extends Type 
class PolyType(val typeParams: List[Symbol], val resultType: Type) extends Type
class WildcardType extends Type

class Symbol
class AnnotationInfo
class Scope



object NarrowableType {
  def unapply(tpe: Type): Boolean =
    (tpe : Type) match {
      case t : TypeRef => true
      case r : RefinedType => true
      case _ => false
    }
}

object UnderlyingType {
  def unapply(tpe: Type): Option[Type] =
    (tpe : Type) match {
      case e : ExistentialType => Some(e.underlying)
      case a : AnnotatedType => Some(a.underlying)
      case p : PolyType => Some(p.resultType)
      case _ => None
    }
}

class Typers {
  
  def isNarrowable(tpe: Type): Boolean = (tpe : Type) match {
    case NarrowableType()           => true
    case UnderlyingType(tpe1: Type) => isNarrowable(tpe1)
    case w : WildcardType           => this.routine() /** in the real implementation you find case _ => !phase.erasedTypes */
  }
  
  
  def routine(): Boolean = true
  
}

object Main {

  def main(args: Array[String]): Unit =
    new Typers().isNarrowable(new TypeRef(new WildcardType(),new Symbol(),Nil)) 
}